﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WarehouseManagement.BusinessLogic.DTO;
using WarehouseManagement.DataAccess;
using WarehouseManagement.Shared;

namespace WarehouseManagement.BusinessLogic.BC
{
    public class POBC : BaseBC
    {
        private UserBC _userBC;
        private MerchandiseBC _merchandiseBC;
        private DataChangedLogBC _dataLogBC;

        public POBC()
        {
            _userBC = new UserBC();
            _merchandiseBC = new MerchandiseBC();
            _dataLogBC = new DataChangedLogBC();

        }
        public decimal GetTotalPOCost(Guid poId)
        {
            var po = Context.POs.SingleOrDefault(e => e.Id == poId);
            if (po != null)
            {
                // Chưa tính đúng
                return po.PODetails.Sum(e => (decimal)e.Quantity * (decimal)e.UnitPriceAfterTax * e.PO.ExchangeRate);
            }
            return 0;
        }

        public decimal GetTotalPOCost(Guid poId, DateTime fromDate, DateTime toDate)
        {
            var po = Context.POs.SingleOrDefault(e => e.Id == poId);
            if (po != null)
            {
                // Chưa tính đúng
                return po.PODetails.Sum(e => (decimal)e.Quantity * (decimal)e.UnitPriceAfterTax * e.PO.ExchangeRate);
            }
            return 0;
        }

        public decimal GetTotalReceivedMoney(string documentNumber, DateTime fromDate, DateTime toDate, DateTime debtDate)
        {

            var imports = Context.Imports.Where(e =>
                (e.CreatedDate >= fromDate && e.CreatedDate.Date <= toDate.Date)
                && e.DocumentNumber == documentNumber);

            //var debtDate = this.GetDateOfLastPeriodDebt(documentNumber);
            //if (debtDate.HasValue)
            {
                imports = imports.Where(e => e.CreatedDate >= debtDate);
            }
            var importVal = 0m;

            if (imports.Count() > 0)
            {
                importVal = imports.Sum(e => e.ImportDetails.Sum(f => (decimal)f.Quantity * (decimal)f.UnitPriceAfterTax * e.ExchangeRate));
            }
            var receiptsVal = this.GetReceiptsValueOf(documentNumber, fromDate, toDate,debtDate);
            return importVal + receiptsVal;

        }

        private PO GetPOEntity(Guid id)
        {
            var po = Context.POs.SingleOrDefault(e => e.Id == id);
            return po;
        }

        public Guid GetPOIdByNumber(string poNumber)
        {
            var obj = (Context.POs.SingleOrDefault(e => e.PONumber == poNumber));
            if (obj == null)
                return Guid.Empty;
            return obj.Id;
        }

        public IEnumerable<PODetailDTO> GetPOList(Guid poId)
        {
            var list = Context.PODetails.Where(e => e.POId == poId)
                .Select(e => ConvertPO(e));
            return list.ToList();
        }

        public IEnumerable<PODetailDTO> GetPOList(string poNumber)
        {
            var id = GetPOIdByNumber(poNumber);
            return GetPOList(id);
        }

        public IEnumerable<ViewPODetailDTO> GetViewPOList(DateTime fromDate, DateTime toDate)
        {
            var list = Context.PODetails.Where(e => e.PO.PODate.Date >= fromDate.Date && e.PO.PODate.Date <= toDate.Date);
            return list.Select(e => new ViewPODetailDTO()
            {
                POId = e.POId,
                CurrencyId = e.PO.CurrencyId,
                CurrencyName = e.PO.Currency.CurrencyName,
                ExchangeRate = e.PO.ExchangeRate,
                MerchandiseCategory = e.Merchandise.MerchandiseCategory.MerchandiseCategoryName,
                MerchandiseId = e.MerchandiseId,
                MerchandiseName = e.Merchandise.MerchandiseName,
                OriginUnitPrice = e.UnitPrice,
                PODate = e.PO.PODate,
                PONumber = e.PO.PONumber,
                Quantity = e.Quantity,
                ShippingFee = e.ShippingFee,
                Tax = e.Tax,
                Unit = e.Merchandise.Unit,
                UnitPriceAfterTax = (decimal)e.UnitPriceAfterTax,

            }).ToList();
        }

        public PODTO GetPOByNumber(string number)
        {
            var obj = (Context.POs.SingleOrDefault(e => e.PONumber == number));
            if (obj != null) return ConvertPO(obj);
            return null;
        }

        public void UpdatePODetail(PODetailDTO item)
        {
            var importDetail = Context.PODetails.SingleOrDefault(e => e.Id == item.Id);

            //importDetail.LastModifiedDate = DateTime.Now;
            importDetail.UnitPrice = item.UnitPrice;
            importDetail.Quantity = item.Quantity;
            importDetail.ShippingFee = item.ShippingFee;
            importDetail.Tax = item.Tax;
            importDetail.Merchandise = Context.Merchandises.SingleOrDefault(e => e.Id == item.MerchandiseId);
            this.SubmitChanges();


        }

        public void UpdatePODetails(IEnumerable<PODetailDTO> items, PODTO master)
        {
            var import = Context.POs.SingleOrDefault(e => e.PONumber == master.PONumber);
            if (import != null)
            {
                //import.ImportReason = master.ImportReason;
                import.Notes = master.Notes;
                import.Currency = Context.Currencies.SingleOrDefault(e => e.Id == master.CurrencyId);
                import.ExchangeRate = master.ExchangeRate;
            }
            SubmitChanges();

            foreach (var item in items)
            {
                UpdatePODetail(item);
            }

            UpdatePO(master);
        }

        public void DeletePODetail(Guid detailId)
        {
            var item = Context.PODetails.SingleOrDefault(e => e.Id == detailId);
            if (item != null)
            {


                Context.PODetails.DeleteOnSubmit(item);
                this.SubmitChanges();

            }
        }

        public void DeletePODetails(IEnumerable<Guid> detailIds)
        {
            foreach (var id in detailIds)
            {
                DeletePODetail(id);
            }

        }

        private void DeletePO(Guid poId)
        {
            var import = Context.POs.SingleOrDefault(e => e.Id == poId);
            if (import != null)
            {
                Context.POs.DeleteOnSubmit(import);
                SubmitChanges();
            }
        }
        public void SavePO(PODTO masterObj, IEnumerable<PODetailDTO> submittedRecords, bool editMode = false)
        {
            try
            {
                this.BeginTransaction();

                var allItems = Context.PODetails.Where(e => e.PO.PONumber == masterObj.PONumber)
                    .Select(e => ConvertPO(e))
                    .ToList();

                var addedItems = GetAddedItems(allItems, submittedRecords).ToList();
                var deletedItems = GetDeletedItems(allItems, submittedRecords).ToList();
                var needUpdatedItems = GetNeedUpdatedItems(addedItems, deletedItems, submittedRecords).ToList();

                if (addedItems.Count > 0)
                {
                    this.AddPO(masterObj, addedItems);
                }
                if (editMode)
                {
                    if (needUpdatedItems.Count > 0)
                    {
                        this.UpdatePODetails(needUpdatedItems, masterObj);
                    }
                    if (deletedItems.Count > 0)
                    {
                        this.DeletePODetails(deletedItems.Select(e => e.Id));
                        var importInDb = Context.POs.SingleOrDefault(e => e.Id == masterObj.Id);
                        if (importInDb.PODetails.Count == 0)
                            DeletePO(importInDb.Id);
                    }
                }


                this.CommitTransaction();
            }
            catch (Exception ex)
            {
                this.RollbackTransaction();
                throw ex;
            }
        }

        public void UpdatePO(PODTO dto)
        {
            var entity = Context.POs.SingleOrDefault(e => e.Id == dto.Id);
            if (entity == null) return;
            //entity.ImportReason = dto.ImportReason;
            entity.Notes = dto.Notes;
            SubmitChanges();
        }

        public void AddPO(PODTO po, IEnumerable<PODetailDTO> details)
        {

            if (!Context.POs.Any(e => e.PONumber == po.PONumber))
            {
                var importEntity = ConvertPO(po);
                Context.POs.InsertOnSubmit(importEntity);
                this.SubmitChanges();
            }
            else
            {
                UpdatePO(po);
            }

            foreach (var item in details)
            {
                item.POId = po.Id;
                //item.CreatedDate = DateTime.Now;
                //item.LastModifiedDate = item.CreatedDate;
                //item.CreatedBy = GlobalMemory.CurrentLoginUser;

                var d = ConvertPO(item);

                if (!Context.PODetails.Any(e => e.Id == item.Id))
                {
                    Context.PODetails.InsertOnSubmit(d);
                    this.SubmitChanges();
                }




            }

        }

        private IEnumerable<PODetailDTO> GetDeletedItems(IEnumerable<PODetailDTO> allItems, IEnumerable<PODetailDTO> submitted)
        {
            var idList = allItems.Select(e => e.Id)
                .Except(submitted.Select(e => e.Id));
            foreach (var id in idList)
            {
                yield return allItems.SingleOrDefault(e => e.Id == id);
            }
        }

        private IEnumerable<PODetailDTO> GetAddedItems(IEnumerable<PODetailDTO> allItems, IEnumerable<PODetailDTO> submitted)
        {
            var idList = submitted.Select(e => e.Id)
                .Except(allItems.Select(e => e.Id));
            foreach (var id in idList)
            {
                yield return submitted.SingleOrDefault(e => e.Id == id);
            }
        }

        private IEnumerable<PODetailDTO> GetNeedUpdatedItems(
            IEnumerable<PODetailDTO> added,
            IEnumerable<PODetailDTO> deleted,
            IEnumerable<PODetailDTO> submitted)
        {
            var addedAndDeleted = added.Select(e => e.Id).Union(deleted.Select(e => e.Id));
            var needUpdatedIds = submitted.Select(e => e.Id).Except(addedAndDeleted);
            foreach (var id in needUpdatedIds)
            {
                yield return submitted.SingleOrDefault(e => e.Id == id);
            }
        }


        public string GenerateNewPONumber()
        {
            decimal no = 1;
            var today = DateTime.Now;
            var prefix = "PO";

            var list = Context.POs.OrderByDescending(e => e.CreatedDate).ToList();
            if (list.Count() > 0)
            {
                var first = list.FirstOrDefault();
                if (first != null)
                {
                    var lastNo = decimal.Parse(first.PONumber.Substring(10, 6));
                    if (first.CreatedDate.Month == today.Month && first.CreatedDate.Year == today.Year)
                        no = lastNo + 1;

                }
            }

            var newNumber = NumberGenerator.GenerateNumber(prefix, today.Month, today.Year, no);
            return newNumber;

        }

        private PODetailDTO ConvertPO(PODetail entity)
        {
            return new PODetailDTO()
            {
                Id = entity.Id,
                MerchandiseId = entity.MerchandiseId,
                POId = entity.POId,
                Quantity = entity.Quantity,
                MerchandiseCategory = entity.Merchandise.MerchandiseCategory.MerchandiseCategoryName,
                MerchandiseName = entity.Merchandise.MerchandiseName,
                ShippingFee = entity.ShippingFee,
                Tax = entity.Tax,
                UnitPrice = entity.UnitPrice,
                MerchandiseUnit = entity.Merchandise.Unit,
                ExchangeRate = entity.PO.ExchangeRate,
                PO = ConvertPO(entity.PO)
            };
        }

        private PODetail ConvertPO(PODetailDTO entity)
        {
            return new PODetail()
            {
                Id = entity.Id,
                MerchandiseId = entity.MerchandiseId,
                POId = entity.POId,
                Quantity = entity.Quantity,
                ShippingFee = entity.ShippingFee,
                Tax = entity.Tax,
                UnitPrice = entity.UnitPrice,
                //UnitPriceAfterTax = entity.UnitPriceAfterTax,                 
            };
        }

        private PODTO ConvertPO(PO entity)
        {
            return new PODTO()
            {
                CreatedBy = entity.CreatedBy,
                CreatedDate = entity.CreatedDate,
                CurrencyId = entity.CurrencyId,
                ExchangeRate = entity.ExchangeRate,
                Id = entity.Id,
                Notes = entity.Notes,
                Paid = entity.Paid,
                PODate = entity.PODate,
                PONumber = entity.PONumber,
                SupplierId = entity.SupplierId,
                SupplierName = entity.Supplier.SupplierName,
                CurrencyName = entity.Currency.CurrencyName
            };
        }

        private PO ConvertPO(PODTO dto)
        {
            return new PO()
            {
                CreatedBy = dto.CreatedBy,
                CreatedDate = dto.CreatedDate,
                CurrencyId = dto.CurrencyId,
                ExchangeRate = dto.ExchangeRate,
                Id = dto.Id,
                Notes = dto.Notes,
                Paid = dto.Paid,
                PODate = dto.PODate,
                PONumber = dto.PONumber,
                SupplierId = dto.SupplierId,
            };
        }
    }
}
